package parallelSRC

import spinal.core.{SFix, SInt, when, _}
import spinal.lib._

object ShiftRegChain{
  def RegModule[T <: Data](input: Vec[T], cond: Bool = null, whenToShift: Bool = null, init: T = null.asInstanceOf[T]): T ={
    val ShiftReg = Reg(input(1), init)
    when(whenToShift){
      ShiftReg := Mux(cond, input(0), input(1))
    }
    ShiftReg.setName("ShiftRegWithMux")
  }

  def apply[T <: Data](inputTwo: T, inputOne: T, length: Int, when: Bool, whenToShift: Bool = null, init: T = null): Vec[T] = {
    def builder(that: T, thatTwo: T, left: Int): List[T] = {
      val input = Vec(thatTwo, that)
      left match {
        case 0 => Nil
        case 1 => that :: Nil
        case _ => that :: builder(RegModule(input, when, whenToShift, init = init), that, left - 1)
      }
    }
    val inputShift = RegModule(Vec(inputOne, inputTwo), when, whenToShift, init = init)
    Vec(builder(inputShift, inputTwo, length))
  }
}

case class ShiftRegTest(taps : Int, width : Int) extends Component {
  //Σ(k=0->order-1)h(k)x(n-k)
  //Multiple and summary
  val io = new Bundle {
    val input = slave Stream (SFix(width - 1 exp, width bits))
    val output = master Stream (SFix(width - 1 exp, width bits))
  }

  io.input.ready := True
  io.output.valid := True
  val inputReg = Reg(SFix(width - 1 exp, width bits)) init (0)
  when(io.input.ready) {
    inputReg := io.input.payload
  }
  val serialToParallel = History(that = inputReg, length = 2, when = io.input.ready, init = toSFix(0))
  val regCom = ShiftRegChain(serialToParallel(0), serialToParallel(1),taps, when = io.output.ready)

  io.output.payload := regCom(regCom.length - 1)
}

object ShiftRegConfig extends SpinalConfig(defaultConfigForClockDomains = ClockDomainConfig(resetKind = SYNC))
object ShiftRegVerilogWithCustomConfig {
  def main(args: Array[String]) {
    ShiftRegConfig.generateVerilog(new ShiftRegTest(4, 8))
  }
}